package com.zhss.demo.register.server;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Objects;

/**
 * 注册表
 * @author gubatian
 * @date 2021/9/7 5:19 下午
 */
public class ServiceRegistry {
    /**
     * 服务实例变更队列检查间隔
     */
    public final static long RECENTLY_CHANGED_ITEM_CHECK_INTERVAL = 3000L;
    /**
     * 最近三分钟变更的服务实例间隔
     */
    public final static long RECENTLY_CHANGED_ITEM_EXPIRED = 3 * 60 * 1000;
    /**
     * 注册表是一个单例类
     */
    public static ServiceRegistry instance = new ServiceRegistry();

    /**
     * 核心的内存数据结构：注册表
     * Map：key指服务名称，value是这个服务的所有的服务实例
     * Map<String, ServiceInstance>：key指服务实例ID，value则是服务实例信息
     */
    private Map<String, Map<String, ServiceInstance>> registry = new HashMap<>();
    /**
     * 最近变更的服务实例队列
     */
    private LinkedList<RecentlyChangedServiceInstance> recentlyChangedQueue = new LinkedList<>();

    private ServiceRegistry() {
        // 启动后台线程监控变更的服务队列
        RecentlyChangedQueueMonitor recentlyChangedQueueMonitor =
                new RecentlyChangedQueueMonitor();
        recentlyChangedQueueMonitor.setDaemon(true);
        recentlyChangedQueueMonitor.start();
    }

    public static ServiceRegistry getInstance() {
        return instance;
    }
    /**
     * 服务注册
     * @param serviceInstance 服务实例
     */
    public synchronized void register(ServiceInstance serviceInstance) {
        // 将服务变更操作放入队列
        RecentlyChangedServiceInstance recentlyChangedServiceInstance = new RecentlyChangedServiceInstance(
                serviceInstance,
                System.currentTimeMillis(),
                ServiceInstanceOperation.REGISTER);
        recentlyChangedQueue.offer(recentlyChangedServiceInstance);
        System.out.println("最近变更队列：" + recentlyChangedQueue);

        Map<String, ServiceInstance> serviceInstanceMap = registry.get(serviceInstance.getServiceInstanceId());
        if (Objects.isNull(serviceInstanceMap)) {
            serviceInstanceMap = new HashMap<>();
            registry.put(serviceInstance.getServiceName(), serviceInstanceMap);
        }

        serviceInstanceMap.put(serviceInstance.getServiceInstanceId(), serviceInstance);

        System.out.println("服务实例【" + serviceInstance + "】，完成注册......");
        System.out.println("注册表：" + registry);
    }

    /**
     * 获取服务实例信息
     * @param serviceName 服务名称
     * @param serviceInstanceId 服务实例ID
     * @return 服务实例
     */
    public synchronized ServiceInstance getServiceInstance(String serviceName, String serviceInstanceId) {
        Map<String, ServiceInstance> serviceInstanceMap = registry.get(serviceName);
        if (Objects.isNull(serviceInstanceMap)) {
            return null;
        }
        return serviceInstanceMap.get(serviceInstanceId);
    }

    /**
     * 获取整个注册表
     * @return
     */
    public synchronized Map<String, Map<String, ServiceInstance>> getRegistry() {
        return registry;
    }

    /**
     * 获取最近有变化的注册表
     * @return
     */
    public synchronized LinkedList<RecentlyChangedServiceInstance> getRecentlyChangedQueue() {
        return recentlyChangedQueue;
    }
    /**
     * 从注册表种删除一个服务实例
     * @param serviceName
     * @param serviceInstanceId
     */
    public synchronized void remove(String serviceName, String serviceInstanceId) {
        System.out.println("服务实例从注册表中进行摘除【" + serviceName + ", " + serviceInstanceId + "】");
        Map<String, ServiceInstance> serviceInstanceMap = registry.get(serviceName);
        if (Objects.isNull(serviceInstanceMap)) {
            return;
        }

        RecentlyChangedServiceInstance recentlyChangedServiceInstance = new RecentlyChangedServiceInstance(
                serviceInstanceMap.get(serviceInstanceId), System.currentTimeMillis(), ServiceInstanceOperation.REMOVE);
        recentlyChangedQueue.offer(recentlyChangedServiceInstance);
        System.out.println("最近变更队列：" + recentlyChangedQueue);

        serviceInstanceMap.remove(serviceInstanceId);

    }

    /**
     * 获取最近变化的注册表
     * @return
     */
    public DeltaRegistry getDeltaRegistry() {
        Long totalCount = 0L;
        for(Map<String, ServiceInstance> serviceInstanceMap : registry.values()) {
            totalCount += serviceInstanceMap.size();
        }

        DeltaRegistry deltaRegistry = new DeltaRegistry(
                recentlyChangedQueue, totalCount);

        return deltaRegistry;
    }

    /**
     * 最近变更的服务实例
     */
    class RecentlyChangedServiceInstance {
        /**
         * 服务实例
         */
        ServiceInstance serviceInstance;
        /**
         * 变更时间
         */
        long changedTimeStamp;
        /**
         * 服务变更操作
         */
        String serviceInstanceOperation;
        public RecentlyChangedServiceInstance(ServiceInstance serviceInstance, long changedTimeStamp, String serviceInstanceOperation) {
            this.serviceInstance = serviceInstance;
            this.changedTimeStamp = changedTimeStamp;
            this.serviceInstanceOperation = serviceInstanceOperation;
        }

        @Override
        public String toString() {
            return "RecentlyChangedServiceInstance{" +
                    "serviceInstance=" + serviceInstance +
                    ", changedTimeStamp=" + changedTimeStamp +
                    ", serviceInstanceOption='" + serviceInstanceOperation + '\'' +
                    '}';
        }
    }

    /**
     * 服务实例操作
     */
    class ServiceInstanceOperation {
        /**
         * 服务注册
         */
        public final static String REGISTER = "register";
        /**
         * 服务删除
         */
        public static final String REMOVE = "remove";
    }

    class RecentlyChangedQueueMonitor extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (instance) {
                    try {
                        RecentlyChangedServiceInstance recentlyChangedItem = null;
                        long currentTime = System.currentTimeMillis();
                        //
                        while ((recentlyChangedItem = recentlyChangedQueue.peek()) != null) {
                            // 3分钟前变更的服务实力出队
                            if (currentTime - recentlyChangedItem.changedTimeStamp >
                                    RECENTLY_CHANGED_ITEM_EXPIRED) {
                                recentlyChangedQueue.pop();
                            }
                        }
                        Thread.sleep(RECENTLY_CHANGED_ITEM_CHECK_INTERVAL);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }


            }
        }
    }
}
